package xyz.xtt.asset.service.impl;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static xyz.xtt.common.asset.AssertUtils.isFalse;
import static xyz.xtt.common.asset.AssertUtils.isNull;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.primitives.Longs;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.api.enums.AssetTypeEnum;
import xyz.xtt.asset.api.enums.BizTypeEnum;
import xyz.xtt.asset.api.enums.DealStateEnum;
import xyz.xtt.asset.api.enums.OwnerTypeEnum;
import xyz.xtt.asset.api.enums.SellPatternEnum;
import xyz.xtt.asset.client.request.TransferActionRequest;
import xyz.xtt.asset.client.request.TransferFromRequest;
import xyz.xtt.asset.client.response.TransferFromResponse;
import xyz.xtt.asset.constants.AssetCache;
import xyz.xtt.asset.constants.ErrorMessage;
import xyz.xtt.asset.entity.TblAsset;
import xyz.xtt.asset.entity.TblAssetAccount;
import xyz.xtt.asset.entity.TblAssetExt;
import xyz.xtt.asset.entity.TblAssetGroup;
import xyz.xtt.asset.entity.TblAssetGroupMember;
import xyz.xtt.asset.entity.TblAssetStaticResource;
import xyz.xtt.asset.entity.TblAssetTransaction;
import xyz.xtt.asset.enums.OrderTypeEnum;
import xyz.xtt.asset.enums.PayUnitEnum;
import xyz.xtt.asset.mapper.TblAssetTransactionMapper;
import xyz.xtt.asset.request.GiftRequest;
import xyz.xtt.asset.response.TransactionResponse;
import xyz.xtt.asset.service.IInnerService;
import xyz.xtt.asset.service.INftService;
import xyz.xtt.asset.service.ITblAssetAccountService;
import xyz.xtt.asset.service.ITblAssetGroupMemberService;
import xyz.xtt.asset.service.ITblAssetGroupService;
import xyz.xtt.asset.service.ITblAssetService;
import xyz.xtt.asset.service.ITblAssetTransactionService;
import xyz.xtt.asset.service.IThirdService;
import xyz.xtt.asset.vo.AssetBaseInfo;
import xyz.xtt.asset.vo.AssetTypeSubItem;
import xyz.xtt.asset.vo.CreateGorupResult;
import xyz.xtt.asset.vo.HotTopAssetVo;
import xyz.xtt.common.asset.AssertUtils;
import xyz.xtt.pay.api.enums.PayOrderBusinessTypeEnum;
import xyz.xtt.pay.api.enums.PayOrderStatusEnum;
import xyz.xtt.user.client.response.UserInfoResponse;

/**
 * <p>
 * 资产交易记录表 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-10-28
 */
@RefreshScope
@Slf4j
@Service
public class TblAssetTransactionServiceImpl extends ServiceImpl<TblAssetTransactionMapper, TblAssetTransaction> implements ITblAssetTransactionService {
	@Autowired
	private IInnerService iInnerService;
//	@Autowired
//	private ITblUserService iTblUserService;
//	@Autowired
//	private ITblUserChainAccountService iTblUserChainAccountService;
	@Autowired
	private ITblAssetAccountService iTblAssetAccountService;
	@Autowired
	private ITblAssetService iTblAssetService;

	@Autowired
	private INftService iNftService;
	@Autowired
	private ITblAssetGroupService iTblAssetGroupService;
	@Autowired
	private ITblAssetTransactionService iTblAssetTransactionService;
	@Autowired
	private ITblAssetGroupMemberService iTblAssetGroupMemberService;
	// 赠送上限
	@Value("${asset.gift.send.limit:3}")
	private Long giftSendLimit;
	// 接收上限
	@Value("${asset.gift.recv.limit:3}")
	private Long giftRecvLimit;
	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	private IThirdService iThirdService;

	@Override
	public List<TblAssetTransaction> queryPaySync(String buyUserId, Set<String> keySet, DealStateEnum state) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(baseMapper);
		return query
				.eq(TblAssetTransaction::getBuyerUserId, buyUserId)
				.in(TblAssetTransaction::getGoodsId, keySet)
				.eq(TblAssetTransaction::getDealState, state.code())
				.list();
	}

	/**
	 * @param orderNo
	 * @param tradeNo
	 * @param targetAssetIds
	 * @param oldState
	 * @param newState
	 * @return
	 */
	@Override
	public boolean updateBuyOrderState(String buyUserId, String orderNo, String tradeNo, Collection<Long> targetAssetIds, DealStateEnum oldState,
			DealStateEnum newState) {
		LambdaUpdateChainWrapper<TblAssetTransaction> updateWrapper = new LambdaUpdateChainWrapper<>(baseMapper);
		updateWrapper
				.in(TblAssetTransaction::getAssetId, targetAssetIds)
				.eq(TblAssetTransaction::getDealState, oldState.code())
				.eq(TblAssetTransaction::getBuyerUserId, buyUserId)
				.set(TblAssetTransaction::getDealState, newState.code())
				.set(TblAssetTransaction::getTradeNo, tradeNo)
				.set(TblAssetTransaction::getOrderNo, orderNo)
				.set(TblAssetTransaction::getPayState, newState.code())
				.set(TblAssetTransaction::getDealFinishDatetime, System.currentTimeMillis());
		return updateWrapper.update();
	}

	/**
	 * 更新交易记录的状态
	 *
	 * @param buyUserId
	 * @param orderId
	 * @param orderNo
	 * @param tradeNo
	 * @param oldState
	 * @param newState
	 * @return
	 */
	@Override
	public boolean updateSingleOrderState(String buyUserId, Long orderId, String orderNo, String tradeNo, DealStateEnum oldState, DealStateEnum newState) {
		LambdaUpdateChainWrapper<TblAssetTransaction> updateWrapper = new LambdaUpdateChainWrapper<>(baseMapper);
		updateWrapper
				.eq(TblAssetTransaction::getOrderId, orderId)
				.eq(TblAssetTransaction::getDealState, oldState.code())
				.eq(TblAssetTransaction::getBuyerUserId, buyUserId)
				.set(TblAssetTransaction::getDealState, newState.code())
				.set(TblAssetTransaction::getTradeNo, tradeNo)
				.set(TblAssetTransaction::getOrderNo, orderNo)
				.set(TblAssetTransaction::getPayState, newState.code())
				.set(TblAssetTransaction::getDealFinishDatetime, System.currentTimeMillis());
		return updateWrapper.update();
	}

	/**
	 * 更新交易记录的支付状态
	 *
	 * @param buyUserId
	 * @param orderId
	 * @param orderNo
	 * @param tradeNo
	 * @param oldState
	 * @param newState
	 * @return
	 */
	@Override
	public boolean updateSinglePayState(String buyUserId, Long orderId, String orderNo, String tradeNo, DealStateEnum oldState, DealStateEnum newState) {
		LambdaUpdateChainWrapper<TblAssetTransaction> updateWrapper = new LambdaUpdateChainWrapper<>(baseMapper);
		updateWrapper
				.eq(TblAssetTransaction::getOrderId, orderId)
				.eq(TblAssetTransaction::getPayState, oldState.code())
				.eq(TblAssetTransaction::getBuyerUserId, buyUserId)
				.set(TblAssetTransaction::getTradeNo, tradeNo)
				.set(TblAssetTransaction::getOrderNo, orderNo)
				.set(TblAssetTransaction::getPayState, newState.code())
				.set(TblAssetTransaction::getDealFinishDatetime, System.currentTimeMillis());
		return updateWrapper.update();
	}

	@Override
	public List<TblAssetTransaction> queryPaySync(String buyUserId, Long orderId) {
		return new LambdaQueryChainWrapper<>(baseMapper).eq(TblAssetTransaction::getOrderId, orderId).eq(TblAssetTransaction::getBuyerUserId, buyUserId).list();
	}

	@Override
	public TblAssetTransaction queryPaySyncOne(String buyUserId, Long orderId) {
		return new LambdaQueryChainWrapper<>(baseMapper).eq(TblAssetTransaction::getOrderId, orderId).eq(TblAssetTransaction::getBuyerUserId, buyUserId).one();
	}

	@Override
	public boolean cacelOrder(String buyUserId, Collection<Long> targetAssetIds, DealStateEnum oldState, DealStateEnum newState) {
		LambdaUpdateChainWrapper<TblAssetTransaction> updateWrapper = new LambdaUpdateChainWrapper<>(baseMapper);
		updateWrapper
				.in(TblAssetTransaction::getAssetId, targetAssetIds)
				.ne(TblAssetTransaction::getBuyerUserId, buyUserId)
				.eq(TblAssetTransaction::getDealState, oldState.code())
				.set(TblAssetTransaction::getDealState, newState.code())
				.set(TblAssetTransaction::getDealFinishDatetime, System.currentTimeMillis());
		return updateWrapper.update();
	}

	@Override
	public List<TransactionResponse> transaction(String uid, String type, Integer currPage, Integer pageSize) {
		AssetTypeEnum assetType = AssetTypeEnum.convert(type);
		currPage = Optional.ofNullable(currPage).filter(Objects::nonNull).filter(x -> x.intValue() >= 0).orElse(0);
		if (!(Objects.equals(assetType, AssetTypeEnum.GROUND) || Objects.equals(assetType, AssetTypeEnum.BIZ_CERTIFICATE))) {
			return Lists.newArrayList();
		}
		MPJLambdaWrapper<TblAsset> leftJoin = JoinWrappers
				.lambda(TblAsset.class)
				.selectAs(TblAsset::getGoodsId, TransactionResponse::getGoodsId)
				.selectAs(TblAsset::getAssetName, TransactionResponse::getAssetName)
				.selectAs(TblAssetExt::getChainTxHash, TransactionResponse::getChainAddress)
				.selectAs(TblAsset::getUnit, AssetTypeSubItem::getPriceType)
				.selectAs(TblAssetTransaction::getDealFinishDatetime, TransactionResponse::getDealFinishDatetime)
				.selectAs(TblAssetTransaction::getUnit, TransactionResponse::getUnit)
				.selectAs(TblAssetTransaction::getAmount, TransactionResponse::getAmount)
				.selectAs(TblAssetTransaction::getSellerUserId, TransactionResponse::getSellerUserId)
				.selectAs(TblAssetTransaction::getBuyerUserId, TransactionResponse::getBuyerUserId)
				.selectAs(TblAssetTransaction::getPrice, TransactionResponse::getPrice)
				.selectAs(TblAssetStaticResource::getDisplayUri, TransactionResponse::getDisplayUri)
				.leftJoin(TblAssetExt.class, TblAssetExt::getAssetId, TblAsset::getId)
				.leftJoin(TblAssetStaticResource.class, TblAssetStaticResource::getAssetId, TblAsset::getId)
				.leftJoin(TblAssetTransaction.class, TblAssetTransaction::getAssetId, TblAsset::getId)
				.eq(TblAsset::getFirstType, assetType.code())
				.eq(TblAssetTransaction::getDealState, DealStateEnum.PAID.code())
				.and(x -> x.eq(TblAssetTransaction::getSellerUserId, uid).or().eq(TblAssetTransaction::getBuyerUserId, uid))
				.orderByAsc(TblAssetTransaction::getDealFinishDatetime);
		Page<TransactionResponse> page = leftJoin.page(new Page<TransactionResponse>(currPage, pageSize), TransactionResponse.class);
		Set<String> uids = new HashSet<>();
		List<TransactionResponse> records = page.getRecords();
		records.stream().forEach(x -> {
			uids.add(x.getSellerUserId());
			uids.add(x.getBuyerUserId());
		});
		if (CollectionUtil.isNotEmpty(uids)) {
			List<UserInfoResponse> userInfo = iInnerService.userInfo(uids.stream().map(Longs::tryParse).collect(toList()));
			Map<Long, UserInfoResponse> userMap = userInfo.stream().collect(toMap(UserInfoResponse::getAppUserId, Function.identity(), (a, b) -> a));
			for (TransactionResponse item : records) {
				item.setPrice(new BigDecimal(item.getPrice()).stripTrailingZeros().toPlainString());
				item.setAmount(new BigDecimal(item.getAmount()).stripTrailingZeros().toPlainString());
				fillItem(item, userMap);
			}
		}
		return records;
	}

	private void fillItem(TransactionResponse item, Map<Long, UserInfoResponse> userMap) {
		Long buyerUserId = Longs.tryParse(item.getBuyerUserId());
		Long sellerUserId = Longs.tryParse(item.getSellerUserId());
		UserInfoResponse buyUser = Optional.ofNullable(userMap.get(buyerUserId)).filter(Objects::nonNull).orElse(null);
		UserInfoResponse sellUser = Optional.ofNullable(userMap.get(sellerUserId)).filter(Objects::nonNull).orElse(null);
		fillUser(item, buyUser, sellUser);
	}

	private void fillUser(TransactionResponse item, UserInfoResponse buyUser, UserInfoResponse sellUser) {
		if (Objects.nonNull(buyUser)) {
			String img = buyUser.fieldVal(UserInfoResponse::getAvatarUri);
			String name = buyUser.fieldVal(UserInfoResponse::getNickname);
			item.setBuyerUserImg(img);
			item.setBuyerUserName(name);
		}
		if (Objects.nonNull(sellUser)) {
			String img = sellUser.fieldVal(UserInfoResponse::getAvatarUri);
			String name = sellUser.fieldVal(UserInfoResponse::getNickname);
			item.setSellerUserImg(img);
			item.setSellerUserName(name);
		}
	}

//	@Override
//	public TblAssetTransaction buildTransaction(TblAsset asset, String buyerUserId, DealStateEnum dealState, OrderTypeEnum orderType,
//			SellPatternEnum sellPattern, BigDecimal amount, String orderNo) {
//		return this.buildTransaction(asset, buyerUserId, dealState, orderType, sellPattern, amount, orderNo, BigDecimal.ZERO);
//	}

//	/**
//	 * 构建交易对像
//	 *
//	 * @param asset
//	 * @param buyerUserId
//	 * @param dealState
//	 * @param orderType
//	 * @param sellPattern
//	 * @param amount
//	 * @param orderNo
//	 * @param fees
//	 * @return
//	 */
//	@Override
//	public TblAssetTransaction buildTransaction(TblAsset asset, String buyerUserId, DealStateEnum dealState, OrderTypeEnum orderType,
//			SellPatternEnum sellPattern, BigDecimal amount, String orderNo, BigDecimal fees, BigDecimal orderPrice, String bizType) {
//		return this.buildTransaction(asset, buyerUserId, dealState, orderType, sellPattern, amount, orderNo, fees);
//	}
	private boolean checkValue(Long val) {
		return Objects.nonNull(val) && val.longValue() > 0;
	}

	private void buildTransactionCheckUser(Long ownerId, Long buyUid) {
		if (!checkValue(ownerId) && !checkValue(buyUid)) {
			log.info("owner and buyUid all null");
			isFalse(false, ErrorMessage.USER_NOT_EXIST);
		}
		if (checkValue(ownerId)) {
			UserInfoResponse userInfo = iInnerService.userInfo(ownerId);
//			TblUser sellUserInfo = this.iTblUserService.info(ownerId);
			isNull(userInfo, ErrorMessage.USER_NOT_EXIST);
			isFalse(userInfo.checkStatus(), ErrorMessage.USER_STATUS_EX, () -> log.info("sellUser uid:{}", userInfo.getAppUserId()));
		}
		if (checkValue(buyUid)) {
			UserInfoResponse userInfo = iInnerService.userInfo(buyUid);
			isNull(userInfo, ErrorMessage.USER_NOT_EXIST);
			isFalse(userInfo.checkStatus(), ErrorMessage.USER_STATUS_EX, () -> log.info("buyUser uid:{}", userInfo.getAppUserId()));
		}
	}

	/**
	 * 构建交易对像
	 *
	 * @param asset
	 * @param buyerUserId
	 * @param dealState
	 * @param orderType
	 * @param sellPattern
	 * @param amount
	 * @param orderNo
	 * @param fees
	 * @param orderPrice
	 * @param bizType
	 * @return
	 */
	@Override
	public TblAssetTransaction buildTransaction(TblAsset asset, String buyerUserId, DealStateEnum dealState, OrderTypeEnum orderType,
			SellPatternEnum sellPattern, BigDecimal amount, String orderNo, BigDecimal fees, BigDecimal orderPrice, BigDecimal actTotalPrice, String bizType) {
		Long ownerId = Longs.tryParse(asset.getOwnerId());
		Long buyUid = Longs.tryParse(buyerUserId);
		HashSet<Long> users = Sets.newHashSet();
		BigDecimal assetAccountPrice = null;
		if (Objects.nonNull(ownerId) && ownerId.longValue() > 0) {
			TblAssetAccount assetAccount = iTblAssetAccountService.account(asset.getId(), ownerId);
			if (assetAccount != null) {
				assetAccountPrice = assetAccount.getPrice();
			}
			users.add(ownerId);
		}
		if (Objects.nonNull(buyUid) && buyUid.longValue() > 0) {
			users.add(buyUid);
		}
		if (CollectionUtil.isEmpty(users)) {
			log.info("buildTransaction sellUser and buyUser both null");
			return null;
		}
//		Map<Long, TblUserChainAccount> queryAfterAddUserMap = Maps.newHashMap();
//		if (users.size() == 1) {
//			Long uid = users.iterator().next();
//			TblUser info = this.iTblUserService.info(uid);
//			isNull(info, ErrorMessage.USER_NOT_EXIST);
//			isFalse(Objects.equals(info.getStatus(), UserStatus.NORMAL.getCode()), ErrorMessage.USER_STATUS_EX, () -> log.info("uid:{}", info.getAppUserId()));
//			TblUserChainAccount accountUser = iTblUserChainAccountService.userChainAccountWithUidInCache(uid);
//			if (Objects.isNull(accountUser)) {
//				log.info("buildTransaction userId:{} chainAccount is null", uid);
//				return null;
//			}
//			queryAfterAddUserMap.put(accountUser.getAppUserId(), accountUser);
//		} else {
		// 检查买方和卖方的帐户情况
		buildTransactionCheckUser(ownerId, buyUid);
//			TblUserChainAccount sellUser = iTblUserChainAccountService.userChainAccountWithUidInCache(ownerId);
//			TblUserChainAccount buyerUser = iTblUserChainAccountService.userChainAccountWithUidInCache(buyUid);
//
//			if (Objects.isNull(sellUser)) {
//				log.info("buildTransaction sellUser userId:{} chainAccount is null", ownerId);
//				return null;
//			}
//			if (Objects.isNull(buyerUser)) {
//				log.info("buildTransaction buyerUser userId:{} chainAccount is null", buyUid);
//				return null;
//			}
//			queryAfterAddUserMap.put(sellUser.getAppUserId(), sellUser);
//			queryAfterAddUserMap.put(buyerUser.getAppUserId(), buyerUser);
//		}
		// 买卖双方的用户必须存在
		TblAssetTransaction build = TblAssetTransaction
				.builder()
				.orderId(IdUtil.getSnowflakeNextId())
				.assetId(asset.getId())
				.sellerUserId(String.valueOf(ownerId))
				.sellerChainAccount(StringUtils.EMPTY)
				.buyerUserId(buyerUserId)
				.buyerChainAccount(StringUtils.EMPTY)
				.dealState(dealState.code())
				.dealCreateDatetime(System.currentTimeMillis())
				.dealFinishDatetime(System.currentTimeMillis())
				.playCreateOrderDatetime(System.currentTimeMillis())
				.payState(dealState.code())
				.orderType(orderType.code())
				.price(assetAccountPrice != null ? assetAccountPrice : asset.getPrice())
				.goodsId(asset.getGoodsId())
				.sellPattern(sellPattern.code())
				.unit(asset.getUnit())
				.amount(amount)
				.orderNo(orderNo)
				.fees(fees)
				.orderPrice(orderPrice)
				.actTotalPrice(actTotalPrice)
				.bizType(bizType)
				.build();
		return build;
	}

	@Override
	public TblAssetTransaction buildTransactionActivity(TblAssetAccount from, TblAssetAccount to, Integer amount, String orderNo, BigDecimal orderPirce,
			String bizType) {
		return buildTransaction(from, to, amount, orderNo, OrderTypeEnum.ACTIVITY, DealStateEnum.PAID, BigDecimal.ZERO, PayUnitEnum.UN_KNOW, orderPirce, bizType);
	}

	@Override
	public TblAssetTransaction buildTransaction(TblAssetAccount from, TblAssetAccount to, Integer num, String orderNo, OrderTypeEnum orderType,
			DealStateEnum dealState, BigDecimal price, PayUnitEnum unit, BigDecimal orderPirce, String bizType) {
		return buildTransaction(from, to.getOwnerId(), num, orderNo, orderType, dealState, price, unit, orderPirce, bizType);
	}

	@Override
	public TblAssetTransaction buildTransaction(TblAssetAccount from, Long toUserId, Integer amount, String orderNo, OrderTypeEnum orderType,
			DealStateEnum dealState, BigDecimal price, PayUnitEnum unit, BigDecimal orderPirce, String bizType) {
		buildTransactionCheckUser(from.getOwnerId(), toUserId);
		Long assetId = from.getAssetId();
		TblAsset asset = this.iTblAssetService.infoWithIdByCache(assetId);
		TblAssetTransaction build = TblAssetTransaction
				.builder()
				.orderId(IdUtil.getSnowflakeNextId())
				.assetId(assetId)
				.sellerUserId(String.valueOf(from.getOwnerId()))
				.sellerChainAccount(StringUtils.EMPTY)
				.buyerUserId(String.valueOf(toUserId))
				.buyerChainAccount(StringUtils.EMPTY)
				.dealState(dealState.code())
				.dealCreateDatetime(System.currentTimeMillis())
				.dealFinishDatetime(System.currentTimeMillis())
				.playCreateOrderDatetime(System.currentTimeMillis())
				.payState(dealState.code())
				.orderType(orderType.code())
				.price(price)
				.goodsId(asset.getGoodsId())
				.sellPattern(SellPatternEnum.NORMAL.code())
				.unit(unit.code())
				.amount(new BigDecimal(String.valueOf(amount)))
				.orderNo(orderNo)
				.orderPrice(orderPirce)
				.bizType(bizType)
				.build();
		return build;
	}

	/**
	 * @param orderNo
	 * @return
	 */
	@Override
	public TblAssetTransaction infoWithOrderNo(String orderNo) {
		if (StringUtils.isBlank(orderNo)) {
			return null;
		}
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		query.eq(TblAssetTransaction::getOrderNo, orderNo);
		return query.one();
	}

	@Override
	public TblAssetTransaction beforeTransactionWithBuyUidAndAssetId(Long buyUid, Long assetId) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		query.eq(TblAssetTransaction::getBuyerUserId, buyUid);
		query.eq(TblAssetTransaction::getAssetId, assetId);
		query.eq(TblAssetTransaction::getOrderType, OrderTypeEnum.ACTIVITY.code());
		query.orderByAsc(TblAssetTransaction::getDealCreateDatetime).last("limit 1");
		return query.one();

	}

	@Override
	public TblAssetTransaction queryTblAssetTransaction(Long id, Long from, Long to) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(this.baseMapper);
		query.eq(TblAssetTransaction::getOrderId, id);
		query.eq(TblAssetTransaction::getSellerUserId, String.valueOf(from));
		query.eq(TblAssetTransaction::getBuyerUserId, String.valueOf(to));
		return query.one();
	}

	@Override
	public Boolean upateTransactionDealState(Long id, Long from, Long to, DealStateEnum oldDealSate, DealStateEnum newDealSate, BigDecimal actTotalPrice) {
		LambdaUpdateChainWrapper<TblAssetTransaction> updateWrapper = new LambdaUpdateChainWrapper<>(baseMapper);
		updateWrapper.eq(TblAssetTransaction::getOrderId, id);
		updateWrapper.eq(TblAssetTransaction::getSellerUserId, String.valueOf(from));
		updateWrapper.eq(TblAssetTransaction::getBuyerUserId, String.valueOf(to));
		updateWrapper.eq(TblAssetTransaction::getDealState, oldDealSate.code());
		updateWrapper.set(TblAssetTransaction::getDealState, newDealSate.code());
		if (Objects.equals(newDealSate, DealStateEnum.PAID) && !Objects.equals(oldDealSate, DealStateEnum.PAID)) {
			updateWrapper.set(TblAssetTransaction::getDealFinishDatetime, System.currentTimeMillis());
			updateWrapper.set(TblAssetTransaction::getActTotalPrice, actTotalPrice);
		}
		return updateWrapper.update();
	}

	@Override
	public Boolean upateTransactionDealState(Long id, DealStateEnum oldDealSate, DealStateEnum newDealSate) {
		LambdaUpdateChainWrapper<TblAssetTransaction> updateWrapper = new LambdaUpdateChainWrapper<>(baseMapper);
		updateWrapper.eq(TblAssetTransaction::getOrderId, id);
		updateWrapper.eq(TblAssetTransaction::getDealState, oldDealSate.code());
		updateWrapper.set(TblAssetTransaction::getDealState, newDealSate.code());
		return updateWrapper.update();
	}

	/**
	 * 查询未支付状态订单
	 *
	 * @param assetId 资产id
	 * @param buyerId 购买者id
	 * @return 订单
	 */
	@Override
	public TblAssetTransaction queryNotPayOrder(Long assetId, long buyerId) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(baseMapper);
		return query
				.eq(TblAssetTransaction::getAssetId, assetId)
				.eq(TblAssetTransaction::getBuyerUserId, String.valueOf(buyerId))
				.eq(TblAssetTransaction::getDealState, DealStateEnum.NOT_PAYED.code())
				.one();
	}

	@Override
	public TblAssetTransaction queryTransaction(Long assetId, String buyerId, OrderTypeEnum orderType) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(baseMapper);
		return query
				.eq(TblAssetTransaction::getAssetId, assetId)
				.eq(TblAssetTransaction::getBuyerUserId, String.valueOf(buyerId))
				.eq(TblAssetTransaction::getDealState, DealStateEnum.PAID.code())
				.eq(TblAssetTransaction::getOrderType, orderType.code())
				.one();
	}

	@Override
	public TblAssetTransaction queryTransaction(TransferActionRequest transferActionRequest) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(baseMapper);
		return query
				.eq(TblAssetTransaction::getSellerUserId, String.valueOf(transferActionRequest.getFrom()))
				.eq(TblAssetTransaction::getBuyerUserId, String.valueOf(transferActionRequest.getTo()))
				.eq(TblAssetTransaction::getDealState, DealStateEnum.NOT_PAYED.code())
				.eq(TblAssetTransaction::getGoodsId, transferActionRequest.getBizId())
				.eq(TblAssetTransaction::getOrderNo, transferActionRequest.getOrderNo())
				.one();
	}

	@Override
	public TblAssetTransaction queryNotPaiedPersonalMintUpChaninOrder(Long assetId, String uid) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(baseMapper);
		return query
				.eq(TblAssetTransaction::getAssetId, assetId)
				.eq(TblAssetTransaction::getBuyerUserId, uid)
				.eq(TblAssetTransaction::getDealState, DealStateEnum.NOT_PAYED.code())
				.eq(TblAssetTransaction::getOrderType, OrderTypeEnum.PERSONAL_MINT.code())
				.one();
	}

	@Override
	public BigDecimal buyMaxPriceWithHistory(String uid, String goodsId) {
		MPJLambdaWrapper<TblAssetTransaction> query = JoinWrappers
				.lambda(TblAssetTransaction.class)
				.selectMax(TblAssetTransaction::getPrice)
				.eq(TblAssetTransaction::getBuyerUserId, uid)
				.eq(TblAssetTransaction::getOrderType, PayOrderBusinessTypeEnum.C_TO_C.getCode())
				.eq(TblAssetTransaction::getDealState, PayOrderStatusEnum.PAY_SUCCESS.getCode())
				.eq(TblAssetTransaction::getGoodsId, String.valueOf(goodsId));
		BigDecimal price = query.one(BigDecimal.class);
		return Objects.isNull(price) ? BigDecimal.ZERO : price;
	}

	/**
	 * 热卖榜， 临时处理
	 * 
	 * @return
	 */
	@Override
	public List<HotTopAssetVo> getHotTopList() {
		QueryWrapper<TblAssetTransaction> query = new QueryWrapper<>();
		query
				.eq("order_type", 0)
				.eq("deal_state", 1)
				.gt("price", 0)
				.groupBy("goods_id")
				.select("goods_id,count(*) as amount")
				.orderByDesc("amount")
				.last("limit 100");
		List<Map<String, Object>> maps = baseMapper.selectMaps(query);
		List<HotTopAssetVo> hotTopAssetVos = new ArrayList<>();
		// 查询订单虚假销量数据
		Map<Long, Long> seqNo = iTblAssetService.getSeqNo();
		// 数据处理
		maps.forEach(m -> {
			String goodsId = m.get("goods_id").toString();
			Long amount = (Long) m.get("amount");
			// 查询 资产信息
			Long markNum = seqNo.get(Longs.tryParse(goodsId));
			amount = Objects.nonNull(markNum) ? markNum : amount;
			AssetBaseInfo assetBaseInfo = iTblAssetService.infoGoodsId(goodsId);
			if (Objects.nonNull(assetBaseInfo)) {
				HotTopAssetVo hotTopAssetVo = new HotTopAssetVo(
						goodsId, assetBaseInfo.getAsset().getAssetName(), assetBaseInfo.getAssetStaticResource().getLogoUri(), amount,
						assetBaseInfo.getAsset().getPrice());
				hotTopAssetVos.add(hotTopAssetVo);
			}
		});
		hotTopAssetVos.sort(Comparator.comparingLong(HotTopAssetVo::getAmount).reversed());
		return hotTopAssetVos;
	}

	@Override
	public TblAssetTransaction transactionInfo(Long orderId) {
		return this.getById(orderId);
	}

	private String giftRedisKey(Long uid, String type) {
		String daySub = DateUtil.format(new Date(), "yyyyMMdd");
		String redisKey = AssetCache.GIFT_LIMT_KEY.genKey(String.valueOf(uid), type, daySub);
		return redisKey;
	}

	private void giftSent(Long uid, GiftRequest giftRequest) {
		Boolean checkFriend = iThirdService.checkFriend(giftRequest.getAsssetId(), uid, giftRequest.getTo());
		isFalse(checkFriend, ErrorMessage.NO_FRIEND_RELATION);
	}

	private boolean checkLimit(String redisKey, Long limit, ErrorMessage errMsg) {
		Long curr = stringRedisTemplate.opsForValue().increment(redisKey, 0);
		stringRedisTemplate.opsForValue().getAndExpire(redisKey, Duration.ofDays(1));
		AssertUtils.isTrue(curr > limit, errMsg);
		return curr > limit;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public TransferFromResponse gift(Long uid, GiftRequest giftRequest) {
		String sendRedisKey = giftRedisKey(uid, "send");
		String recvRedisKey = giftRedisKey(giftRequest.getTo(), "recv");

		checkLimit(sendRedisKey, giftSendLimit, ErrorMessage.GIFT_SEND_LIMIT_EX);
		checkLimit(recvRedisKey, giftRecvLimit, ErrorMessage.GIFT_RECV_LIMIT_EX);
		giftSent(uid, giftRequest);

		Long asssetId = giftRequest.getAsssetId();
		TblAsset asset = iTblAssetService.getById(asssetId);
		isNull(asset, ErrorMessage.ASSET_NOT_EXIST);
		OwnerTypeEnum ownerType = OwnerTypeEnum.queryConvert(asset.getOwnerType());
		isNull(ownerType, ErrorMessage.OWNER_TYPE_ERR, () -> log.info("gift not ownerType:{}", ownerType));
		isFalse(Objects.equals(ownerType, OwnerTypeEnum.USER), ErrorMessage.OWNER_TYPE_ERR, () -> log.info("gift not ownerType:{}", ownerType));
		TransferFromRequest transferFromRequest = TransferFromRequest
				.builder()
				.from(uid)
				.to(giftRequest.getTo())
				.amount(1)
				.price(asset.getPrice().stripTrailingZeros().toPlainString())
				.bizId(asset.getGoodsId())
				.orderType(OrderTypeEnum.GIFT.name())
				.orderNo(IdUtil.getSnowflakeNextIdStr())
				.fees(BigDecimal.ZERO)
				.build();
		TransferFromResponse transferFrom = iNftService.transferFrom(BizTypeEnum.NFT.dbType(), transferFromRequest);
		isNull(transferFrom, ErrorMessage.GIFT_OPERATOR_ERR);
		// 2是玫瑰花
		if (!Objects.equals(2, asset.getSecondType())) {
			TblAssetGroup assetGroup = iTblAssetGroupService.queryAssetGroup(asset.getId());
			if (Objects.isNull(assetGroup)) {
				// 早期有铸造没建群的补群信息
				TblAssetTransaction queryTransaction = iTblAssetTransactionService
						.queryTransaction(asset.getId(), asset.getAuthorId(), OrderTypeEnum.PERSONAL_MINT);
				if (Objects.nonNull(queryTransaction)) {
					log.info("历史交易没查到 giftRequest:{}", JSONUtil.toJsonStr(giftRequest));
					CreateGorupResult createResult = iThirdService
							.createGorup(asset.getId(), queryTransaction.getOrderId(), Longs.tryParse(asset.getAuthorId()), asset.getAssetName());
					if (createResult.isResult()) {
						assetGroup = createResult.getGroup();
					} else {
						log.info("补建群失败 giftRequest:{}", JSONUtil.toJsonStr(giftRequest));
					}
				}
			}
			if (Objects.isNull(assetGroup)) {
				log.info("gift uid:{} transferFrom:{} group is null", uid, JSONUtil.toJsonStr(transferFrom));
				return null;
			}
			TblAssetGroupMember queryGroupMember = iTblAssetGroupMemberService.queryGroupMember(assetGroup.getId(), transferFrom.getTo());
			if (Objects.nonNull(queryGroupMember)) {
				log.info("gift uid:{} joined group:{}", transferFrom.getTo(), assetGroup.getId());
				return null;
			}
			Long orderId = transferFrom.getTransactionOrderId();
			Boolean joinGroup = iThirdService.joinGroup(orderId, asssetId, uid, recvRedisKey, orderId, 1);
			isFalse(joinGroup, ErrorMessage.JOIN_GROUP_EX);
		}

		// 赠送人和接收人都加人
		stringRedisTemplate.opsForValue().increment(sendRedisKey, 1);
		stringRedisTemplate.opsForValue().increment(recvRedisKey, 1);
		return transferFrom;
	}

	@Override
	public TblAssetTransaction queryTransaction(String orderNo, Long form, Long to) {
		LambdaQueryChainWrapper<TblAssetTransaction> query = new LambdaQueryChainWrapper<>(baseMapper);
		return query
				.eq(TblAssetTransaction::getOrderNo, orderNo)
				.eq(TblAssetTransaction::getSellerUserId, String.valueOf(form))
				.eq(TblAssetTransaction::getBuyerUserId, String.valueOf(to))
				.one();
	}

	@Override
	public TblAssetTransaction queryTransactionByAssetId(Long assetId) {
		return new LambdaQueryChainWrapper<>(baseMapper)
				.eq(TblAssetTransaction::getAssetId, assetId)
				.eq(TblAssetTransaction::getOrderType, OrderTypeEnum.MINT.code())
				.one();
	}

//	@Override
//	public TblAssetTransaction buildTransaction(TblAsset asset, String buyerUserId, DealStateEnum dealState, OrderTypeEnum orderType,
//			SellPatternEnum sellPattern, BigDecimal amount, String orderNo, BigDecimal fees) {
//		// TODO Auto-generated method stub
//		 return null;
//	}

}
